import math
import re
from bson.objectid import ObjectId
from database import db as mongo
from preprocess import wordlevel_lemmatize, lemmatize_dict, wordlevel_stem
from score import word_term, random_recommend, get_res_by_probability

def recommend(rec, openId):
    lemmatized = wordlevel_lemmatize(rec.lower())
    user = mongo.user.find_one({"openId": openId})
    reccommend = mongo.recommendation.find_one({"lemmatized": lemmatized})
    if reccommend is None:
        recId = mongo.create_data(lemmatized)
    else:
        recId = reccommend['_id']
        if 'rlist' in user and recId in user['rlist']:
            return f"已经添加了{reccommend['recommender'][str(user['_id'])]}"
    mongo.update_data(recId, user['_id'], rec)
    mongo.update_user(user['_id'], recId)
    return f"{rec}添加成功"

def cancel(rec, openId):
    lemmatized = wordlevel_lemmatize(rec.lower())
    user = mongo.user.find_one({"openId": openId})
    reccommend = mongo.recommendation.find_one({"lemmatized": lemmatized})
    if reccommend is None:
        return f"尚未添加{rec}"
    else:
        recId = reccommend['_id']
        if 'rlist' in user and recId in user['rlist']:
            mongo.update_data(recId, user['_id'], rec, add=False)
            mongo.update_user(user['_id'], recId, add=False)
            return f"{rec}删除成功"
        else:
            return f"尚未添加{rec}"

def show_my_recommendation(openId):
    user = mongo.user.find_one({"openId": openId})
    res = []
    if "rlist" in user and len(user['rlist']):
        rlist = mongo.recommendation.find({"_id":{"$in":user['rlist']}})
        uId = str(user["_id"])
        for r in rlist:
            if uId in r["recommender"]:
                res.append(r["recommender"][uId])
        return ", ".join(res)
    else:
        return "尚未添加过推荐词"

def how_much_recommendation(rec):
    lemmatized = wordlevel_lemmatize(rec.lower())
    reccommend = mongo.recommendation.find_one({"lemmatized": lemmatized})
    if reccommend is None:
        return f"尚未添加{rec}"
    else:
        return f"{len(reccommend['recommender'])}人添加了{rec}"

def subscribe(openId, select=True):
    if select:
        res = mongo.user.update_one({'openId': openId}, {'$set': {'subscribe': True}})
    else:
        res = mongo.user.update_one({'openId': openId}, {'$unset': {'subscribe': ''}})
    return res.modified_count

def recommendation_ranking(sumList):
    reccommend = list(mongo.recommendation.find({}))
    appearIncrease = [0]*len(reccommend)
    conference = ['EMNLP', 'NeurIPS', 'ACL', 'NAACL', 'NAACL', 'CoNLL', 'COLING', 'ICLR', 'AAAI', 'NLPCC']
    for i in range(len(sumList)-1, -1, -1):
        score = 0
        termList = []
        sumList[i]['recommendUpdate'] = []
        if 'summary' in sumList[i]:
            sentence = wordlevel_lemmatize(f'{sumList[i]["summary"]} {sumList[i]["authors"]} {sumList[i]["title"]}'.lower())
        else:
            article = mongo.article.find_one({'rawId':sumList[i]['rawId']})
            if not 'summary' in article:
                sentence = wordlevel_lemmatize(f'{sumList[i]["authors"]} {sumList[i]["title"]}'.lower())
            else:
                sumList[i]['summary'] = article['summary']
                sentence = wordlevel_lemmatize(f'{article["summary"]} {sumList[i]["authors"]} {sumList[i]["title"]}'.lower())
        for j, rec in enumerate(reccommend):
            if rec['lemmatized'] in sentence:
                appearIncrease[j] += 1
                score += len(rec['recommender'])
                termList.append(word_term(rec['appear'], rec['recommend'], rec['amount']))
                sumList[i]['recommendUpdate'].append(j)
        if 'comments' in sumList[i]:
            score = math.floor(score*1.1)
            if not re.search(r'((?:19|20)\d\d)', sumList[i]['comments']) is None:
                score = math.floor(score*1.2)
            if [word for word in conference if word in sumList[i]['comments']]:
                score = math.floor(score*1.3)
        sumList[i]['score'] = score
        sumList[i]['probability'] = random_recommend(termList)
    return reccommend, appearIncrease

def recommendation_highlight(sumList):
    reccommend = list(mongo.recommendation.find({}))
    classification = list(mongo.classification.find({}).sort([("display_order", 1)]))
    appearIncrease = [0]*len(reccommend)
    class_list = {c['domain']: [] for c in classification}
    class_list['其他'] = []
    conference = ['emnlp', 'neurips', 'acl', 'naacl', 'conll', 'coling', 'iclr', 'aaai', 'nlpcc']
    for i in range(len(sumList)-1, -1, -1):
        score = 0
        sumList[i]['shighlight'] = {}
        sumList[i]['thighlight'] = {}
        # 摘要
        summary = None
        if 'summary' in sumList[i]:
            summary, sum_dict = lemmatize_dict(sumList[i]["summary"].lower())
        else:
            article = mongo.article.find_one({'rawId':sumList[i]['rawId']})
            if 'summary' in article:
                sumList[i]['summary'] = article['summary']
                summary, sum_dict = lemmatize_dict(sumList[i]["summary"].lower())
        for j, rec in enumerate(reccommend):
            if summary is not None and rec['lemmatized'] in summary:
                flag = True
                pointer = 0
                sum_start_map = {dict_key[0]: dict_key for dict_key in sum_dict.keys()}
                sum_end_map = {dict_key[1]: dict_key for dict_key in sum_dict.keys()}
                while summary.find(rec['lemmatized'], pointer) != -1:
                    pos_x = summary.find(rec['lemmatized'], pointer)
                    pos_y = pos_x + len(rec['lemmatized'])
                    if pos_x in sum_start_map and pos_y in sum_end_map:
                        if flag:
                            appearIncrease[j] += 1
                            flag = False
                        start, end = sum_dict[sum_start_map[pos_x]][0], sum_dict[sum_end_map[pos_y]][1]
                        sumList[i]['shighlight'][start] = end
                        pointer = pos_y
                    elif 'not_extend' not in rec or rec['not_extend'] == False:
                        pos_x_extend = smax(0, len(sum_start_map)-1, pos_x, sorted(sum_start_map.items()))
                        pos_y_extend = smin(0, len(sum_end_map)-1, pos_y, sorted(sum_end_map.items(), reverse=True))
                        start, end = sum_dict[sum_start_map[pos_x_extend]][0], sum_dict[sum_end_map[pos_y_extend]][1]
                        sumList[i]['shighlight'][start] = end
                        pointer = pos_y_extend
                    else:
                        pointer = pos_x + 1
        # 标题
        if 'title' in sumList[i]:
            # highlight
            title, t_dict = lemmatize_dict(sumList[i]["title"].lower())
            # classification
            title_class = wordlevel_stem(sumList[i]["title"].lower())
        for j, rec in enumerate(reccommend):
            if rec['lemmatized'] in title:
                flag = True
                pointer = 0
                t_start_map = {dict_key[0]: dict_key for dict_key in t_dict.keys()}
                t_end_map = {dict_key[1]: dict_key for dict_key in t_dict.keys()}
                while title.find(rec['lemmatized'], pointer) != -1:
                    pos_x = title.find(rec['lemmatized'], pointer)
                    pos_y = pos_x + len(rec['lemmatized'])
                    if pos_x in t_start_map and pos_y in t_end_map:
                        if flag:
                            appearIncrease[j] += 1
                            flag = False
                        start, end = t_dict[t_start_map[pos_x]][0], t_dict[t_end_map[pos_y]][1]
                        sumList[i]['thighlight'][start] = end
                        pointer = pos_y
                    elif 'not_extend' not in rec or rec['not_extend'] == False:
                        pos_x_extend = smax(0, len(t_start_map)-1, pos_x, sorted(t_start_map.items()))
                        pos_y_extend = smin(0, len(t_end_map)-1, pos_y, sorted(t_end_map.items(), reverse=True))
                        start, end = t_dict[t_start_map[pos_x_extend]][0], t_dict[t_end_map[pos_y_extend]][1]
                        sumList[i]['thighlight'][start] = end
                        pointer = pos_y_extend
                    else:
                        pointer = pos_x + 1
        if 'comments' in sumList[i]:
            score = math.floor(score*1.1)
            if not re.search(r'((?:19|20)\d\d)', sumList[i]['comments']) is None:
                score = math.floor(score*1.2)
            if [word for word in conference if word in sumList[i]['comments'].lower()]:
                score = math.floor(score*1.3)
        sumList[i]['score'] = score

        for j, rec in enumerate(sorted(classification, key=lambda dom: dom['order'])):
            for kw in rec['keywords']:
                if kw in title_class:
                    sumList[i]['domain'] = rec['order']
                    class_list[rec['domain']].append(sumList[i])
                    break
            else:
                continue
            break
        else:
            class_list['其他'].append(sumList[i])
    return reccommend, appearIncrease, class_list

def select_paper_by_probability(prob):
    return get_res_by_probability(prob)

def recommend_addup(reccommend, aIn, rIn, pIn):
    if reccommend['amount']+pIn > 1000:
        if reccommend['turns'] >= 5:
            if reccommend['appear'] == 0:
                for rec in reccommend['recommender'].keys():
                    user = mongo.user.find_one({'_id': ObjectId(rec)})
                    mongo.update_user(user['_id'], reccommend['_id'], add=False)
                mongo.recommendation.remove({'_id': reccommend['_id']})
                return
        reccommend['amount'] = math.ceil((reccommend['amount']+pIn)/10)
        reccommend['recommend'] = math.ceil((reccommend['recommend']+rIn)/10)
        reccommend['appear'] = math.ceil((reccommend['appear']+aIn)/10)
        reccommend['turns'] = reccommend['turns']+1    
    else:
        reccommend['amount'] = reccommend['amount']+pIn
        reccommend['recommend'] = reccommend['recommend']+rIn
        reccommend['appear'] = reccommend['appear']+aIn
    mongo.recommend_update(reccommend['_id'], reccommend['appear'], reccommend['recommend'], reccommend['amount'])

def history(date):
    return mongo.document.find_one({'createDate': date})

def article_recommend(aid):
    _, success = mongo.article_recommend(ObjectId(aid))
    if success:
        return mongo.article.find_one({"_id": ObjectId(aid)})["recommend"]
    else:
        return 0

def article_republish(length:int=10, recommend:int=5):
    a_list = mongo.article_republish_rank()
    length = min(length, len(a_list))
    def bin_div(l: int, r: int, num: int):
        while l < r:
            mid = (l+r+1) >> 1
            if 'recommend' in a_list[mid] and a_list[mid]['recommend'] > num:
                l = mid
            else:
                r = mid-1
        return l
    if 'recommend' in a_list[length-1]:
        if a_list[length-1]['recommend'] >= recommend:
            return a_list[:bin_div(length-1, len(a_list)-1, recommend-1)+1]
        else:
            return a_list[:length]
    else:
        return a_list[:bin_div(0, length-1, 0) + 1]


def recommend_author(author, openId):
    pass

def cancel_author(author, openId):
    pass

def smin(l: int, r: int, q: int, arr: list):
    while l < r:
        mid = (l + r + 1) >> 1
        if arr[mid][0] < q:
            r = mid - 1
        else:
            l = mid
    return arr[l][0]


def smax(l: int, r: int, q: int, arr: list):
    while l < r:
        mid = (l + r + 1) >> 1
        if arr[mid][0] > q:
            r = mid - 1
        else:
            l = mid
    return arr[l][0]



if __name__ == '__main__':
    print(recommend("test", "ou_0f34e41f0bff893f77d6829a3c619d42"))
    print(how_much_recommendation("test"))
